#include "Rp2Image.h"
#include "Rp2BitHacks.h"
#include "Rp2Catalog.h"
#include "Rp2ImageVersion.h"

using namespace Rp2;

RP2_IMPLEMENT_RTTI(Rp2, Image, Object);
RP2_IMPLEMENT_DEFAULT_NAME_ID(Image, Object);
RP2_IMPLEMENT_INITIALIZE(Image);
RP2_IMPLEMENT_TERMINATE(Image);

int Image::ms_aiBytesPerPixel[Image::IT_QUANTITY] =
{
    3,  // IT_RGB888
    4,  // IT_RGBA8888
    4,  // IT_DEPTH16
    4,  // IT_DEPTH24
    4,  // IT_DEPTH32
    3,  // IT_CUBE_RGB888
    4,  // IT_CUBE_RGBA8888
    12, // IT_RGB32F
    16, // IT_RGBA32F
    6,  // IT_RGB16F
    8,  // IT_RGBA16F
    6,  // IT_RGB16I
    8,  // IT_RGBA16I
    1,  // IT_INTENSITY8I
    2,  // IT_INTENSITY16I
    2,  // IT_INTENSITY16F
    4,  // IT_INTENSITY32F
    2,  // IT_RGB565
    2,  // IT_RGBA5551
    2,  // IT_RGBA4444
	3,	// IT_BGR888
	4,  // IT_BGRA8888
};

std::string Image::ms_akFormatName[Image::IT_QUANTITY] =
{
    std::string("IT_RGB888"),
    std::string("IT_RGBA8888"),
    std::string("IT_DEPTH16"),
    std::string("IT_DEPTH24"),
    std::string("IT_DEPTH32"),
    std::string("IT_CUBE_RGB888"),
    std::string("IT_CUBE_RGBA8888"),
    std::string("IT_RGB32F"),
    std::string("IT_RGBA32F"),
    std::string("IT_RGB16F"),
    std::string("IT_RGBA16F"),
    std::string("IT_RGB16I"),
    std::string("IT_RGBA16I"),
    std::string("IT_INTENSITY8I"),
    std::string("IT_INTENSITY16I"),
    std::string("IT_INTENSITY16F"),
    std::string("IT_INTENSITY32F"),
    std::string("IT_RGB565"),
    std::string("IT_RGBA5551"),
    std::string("IT_RGBA4444"),
	std::string("IT_BGR888"),
	std::string("IT_BGRA8888")
};

// all upper case
std::string Image::ms_kIFStringMap[Image::IF_QUANTITY] = 
{
	"WMIF",
	"TGA"
};

std::map<std::string, Image::ImageFormat>* 
	Image::ms_pkFormatMap = 0;

//---------------------------------------------------------------------------------------------------
void Image::Initialize()
{
	assert(ms_pkFormatMap == 0);
	ms_pkFormatMap = new std::map<std::string, Image::ImageFormat>;
	for (int i = 0; i < (int)IF_QUANTITY; i++)
	{
		ms_pkFormatMap->insert(std::make_pair(ms_kIFStringMap[i], ImageFormat(i)));
	}
}
//---------------------------------------------------------------------------------------------------
void Image::Terminate()
{
	delete ms_pkFormatMap;
}
//---------------------------------------------------------------------------------------------------
Image::Image(FormatMode eFormat, int iBound0, unsigned char* aucData,
	const char* acImageName)
{
	assert(acImageName);

    m_eFormat = eFormat;
    m_iDimension = 1;
    m_aiBound[0] = iBound0;
    m_aiBound[1] = 1;
    m_aiBound[2] = 1;
    m_iQuantity = iBound0;
    m_aucData = aucData;
    SetName(acImageName);
    Catalog<Image>::GetActive()->Insert(this);	
}
//---------------------------------------------------------------------------------------------------
Image::Image (FormatMode eFormat, int iBound0, int iBound1,
    unsigned char* aucData, const char* acImageName)
{
    assert(acImageName);

    m_eFormat = eFormat;
    m_iDimension = 2;
    m_aiBound[0] = iBound0;
    m_aiBound[1] = iBound1;
    m_aiBound[2] = 1;
    m_iQuantity = iBound0*iBound1;
    m_aucData = aucData;
    SetName(acImageName);
    Catalog<Image>::GetActive()->Insert(this);
}
//---------------------------------------------------------------------------------------------------
Image::Image (FormatMode eFormat, int iBound0, int iBound1, int iBound2,
    unsigned char* aucData, const char* acImageName)
{
    assert(acImageName);

    m_eFormat = eFormat;
    m_iDimension = 3;
    m_aiBound[0] = iBound0;
    m_aiBound[1] = iBound1;
    m_aiBound[2] = iBound2;
    m_iQuantity = iBound0*iBound1*iBound2;
    m_aucData = aucData;
    SetName(acImageName);
    Catalog<Image>::GetActive()->Insert(this);
}
//---------------------------------------------------------------------------------------------------
Image::Image ()
{
    m_eFormat = IT_QUANTITY;
    m_iDimension = 0;
    m_aiBound[0] = 0;
    m_aiBound[1] = 0;
    m_aiBound[2] = 0;
    m_iQuantity = 0;
    m_aucData = 0;
}
//---------------------------------------------------------------------------------------------------
Image::~Image ()
{
    delete[] m_aucData;

    Catalog<Image>::RemoveAll(this);
}
//---------------------------------------------------------------------------------------------------
void Image::AllocateData ()
{
    if (!m_aucData)
    {
        int iNumBytes = m_iQuantity*ms_aiBytesPerPixel[m_eFormat];
        m_aucData = new unsigned char[iNumBytes];
    }
}
//---------------------------------------------------------------------------------------------------
Image* Image::Load (const std::string& rkImageName)
{
    const char* acDecorated;

	// the file name must include extension.
    std::string kFilename = rkImageName;
    acDecorated = System::GetPath(kFilename.c_str(), System::SM_READ);

    if (!acDecorated)
    {
		System::MsgBoxPrintf(TC("Image::Load"), TC("Image : %s Decorated error"), kFilename.c_str());
        return 0;
    }

	// get format type of image
	ImageFormat eIFormat = GetImageFormat(kFilename);

	Image* pkImage = OnLoad(eIFormat, acDecorated, kFilename);
	return pkImage;
}
//---------------------------------------------------------------------------------------------------
Image::ImageFormat Image::GetImageFormat(const std::string& rkImageName)
{
	std::string kFormatString;
	std::string::size_type uiBegin, uiEnd;

	uiBegin = rkImageName.find_last_of(".");
	uiBegin++; 	// skip over "."
	uiEnd = rkImageName.length();	
	kFormatString = rkImageName.substr(uiBegin, uiEnd - uiBegin);
	// convert to upper case
	System::ToUpperCase(kFormatString);

	std::map<std::string, ImageFormat>::iterator pkIter
		= ms_pkFormatMap->find(kFormatString);
	if (pkIter != ms_pkFormatMap->end())
	{
		// Find the image format
		return pkIter->second;
	}

	// unsupported image format.
	System::MsgBoxPrintf(TC("Image::GetImageFormat"),
		TC("Unsupported image format : %s"),kFormatString.c_str());
	return IF_QUANTITY;
}
//---------------------------------------------------------------------------------------------------
Image* Image::OnLoad(ImageFormat eIFormat, const char* rkDecorated,
		const std::string& rkFilename)
{
	Image* pkImage = 0;
	switch(eIFormat)
	{
		case IF_WMIF:
		{
			pkImage = LoadImageWMIF(rkDecorated, rkFilename);
			break;
		}
		case IF_TGA:
		{
			pkImage = LoadImageTGA(rkDecorated, rkFilename);
			break;
		}
		default: break;
	}
	assert(pkImage);
	return pkImage;
}
//---------------------------------------------------------------------------------------------------
bool Image::Save (const char* acFilename)
{
    if (!acFilename)
    {
        return false;
    }

    FILE* pkFile = System::Fopen(acFilename,"wb");
    if (!pkFile)
    {
        return false;
    }

    // write the file version
    System::Write1(pkFile,ImageVersion::LENGTH,ImageVersion::LABEL);

    // write the image format and dimensions
    int iFormat = (int)m_eFormat;
    System::Write4le(pkFile,1,&iFormat);
    System::Write4le(pkFile,1,&m_iDimension);
    System::Write4le(pkFile,1,&m_aiBound[0]);
    System::Write4le(pkFile,1,&m_aiBound[1]);
    System::Write4le(pkFile,1,&m_aiBound[2]);

    // write the image data
    int iDataSize = ms_aiBytesPerPixel[m_eFormat]*m_iQuantity;
    if (IsCubeImage())
    {
        iDataSize *= 6;
    }
    System::Write1(pkFile,iDataSize,m_aucData);

    System::Fclose(pkFile);
    return true;
}
//---------------------------------------------------------------------------------------------------
ColorRGBA* Image::CreateRGBA () const
{
    if (!IsCubeImage())
    {
        ColorRGBA* akCImage = new ColorRGBA[m_iQuantity];
        CopyRGBA(akCImage);
        return akCImage;
    }

    // Cube maps are handled as six separate images, so there is no need to
    // create an RGBA image here.
    return 0;
}
//---------------------------------------------------------------------------------------------------
void Image::CopyRGBA (ColorRGBA* akCImage) const
{
    const float fInv255 = 1.0f/255.0f;
    ColorRGBA* pkCValue;
    const unsigned char* pucValue;
    int i;

    if (m_eFormat == IT_RGB888)
    {
        pkCValue = akCImage;
        pucValue = (const unsigned char*)m_aucData;
        for (i = 0; i < m_iQuantity; i++, pkCValue++)
        {
            pkCValue->R() = fInv255*(float)(*pucValue++);
            pkCValue->G() = fInv255*(float)(*pucValue++);
            pkCValue->B() = fInv255*(float)(*pucValue++);
            pkCValue->A() = 1.0f;
        }
    }
    else if (m_eFormat == IT_RGBA8888)
    {
        pkCValue = akCImage;
        pucValue = (const unsigned char*)m_aucData;
        for (i = 0; i < m_iQuantity; i++, pkCValue++)
        {
            pkCValue->R() = fInv255*(float)(*pucValue++);
            pkCValue->G() = fInv255*(float)(*pucValue++);
            pkCValue->B() = fInv255*(float)(*pucValue++);
            pkCValue->A() = fInv255*(float)(*pucValue++);
        }
    }
    else if (IsDepthImage())
    {
        pkCValue = akCImage;
        const float* pfValue = (const float*)m_aucData;
        for (i = 0; i < m_iQuantity; i++, pkCValue++, pfValue++)
        {
            float fValue = *pfValue;
            pkCValue->R() = fValue;
            pkCValue->G() = fValue;
            pkCValue->B() = fValue;
            pkCValue->A() = 1.0f;
        }
    }
    else
    {
        // Cube images are handled as six 2D images in the software renderer,
        // so no conversion is needed.

        // TODO. Handle RGB16F, RGBA16F, RGB32F, RGBA32F, RGB16I, RGBA16I,
        // INTENSITY8I, INTENSITY16I, INTENSITY16F, INTENSITY32F.
    }
}
//---------------------------------------------------------------------------------------------------